package com.kedacom.ctsp.authority.service.simple;

import com.google.common.collect.Maps;
import com.kedacom.ctsp.authority.dao.PersonSqlDao;
import com.kedacom.ctsp.authority.dto.PersonImportDTO;
import com.kedacom.ctsp.authority.emuns.SexEnum;
import com.kedacom.ctsp.authority.entity.Department;
import com.kedacom.ctsp.authority.entity.Person;
import com.kedacom.ctsp.authority.service.DepartmentService;
import com.kedacom.ctsp.authority.service.PersonService;
import com.kedacom.ctsp.authority.util.ImportExcelCommonUtil;
import com.kedacom.ctsp.authority.vo.AuthorityImportExcelResultVo;
import com.kedacom.ctsp.authz.entity.Authentication;
import com.kedacom.ctsp.authz.event.PersonChgDepartmentEvent;
import com.kedacom.ctsp.authz.exception.UnauthorizedException;
import com.kedacom.ctsp.lang.RandomUtil;
import com.kedacom.ctsp.lang.StringUtil;
import com.kedacom.ctsp.lang.exception.CommonException;
import com.kedacom.ctsp.lang.mapper.BeanMapper;
import com.kedacom.ctsp.office.excel.ExcelUtil;
import com.kedacom.ctsp.web.service.StatusEnum;
import com.kedacom.ctsp.web.service.simple.AbstractCrudEntityService;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import java.util.*;
import java.util.stream.Collectors;

import static java.util.stream.Collectors.groupingBy;

/**
 * 默认的服务实现
 *
 * @author
 */
@Service("personService")
@CacheConfig(cacheNames = "person")
public class PersonServiceImpl extends AbstractCrudEntityService<Person, String> implements PersonService, ApplicationEventPublisherAware {

    @Autowired
    private DepartmentService departmentService;

    @Autowired
    private PersonSqlDao personSqlDao;


    private ApplicationEventPublisher eventPublisher;

    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher eventPublisher) {
        this.eventPublisher = eventPublisher;
    }

    @Override
    @Transactional(readOnly = true)
    public Person selectByCode(String code) {
        if (org.apache.commons.lang3.StringUtils.isBlank(code)) {
            return null;
        }
        return createQuery().where(Person.CODE, code).single();
    }


    @Override
    public String insert(Person entity) {
        // 人员编号重复
        tryValidateProperty(null == selectByCode(entity.getCode()), Person.CODE, "auth.person.code.duplicated");
        if (entity.getStatus() == null) {
            entity.setStatus(StatusEnum.ENABLE.ordinal());
        }
        return super.insert(entity);
    }


    @Override
    public String patch(String id, Person entity) {
        Person oldPerson = get(entity.getId());
        // 人员编号是否改变
        if (StringUtils.isNotBlank(oldPerson.getCode()) && !oldPerson.getCode().equals(entity.getCode())) {
            tryValidateProperty(null == selectByCode(entity.getCode()), Person.CODE, "auth.person.code.duplicated");
        }

        //原部门编号
        String oldDeptCode = oldPerson.getDepartment() == null ? "" : oldPerson.getDepartment().getCode();

        String updateRt = super.patch(entity.getId(), entity);
        if (StringUtils.isNotBlank(updateRt)) {
            //检查是否换部门
            checkPersonChgDepartment(entity, oldDeptCode);
        }
        return updateRt;

    }

    @Override
    public String update(String id, Person entity) {

        Person oldPerson = get(entity.getId());
        // 人员编号是否改变
        if (StringUtils.isNotBlank(oldPerson.getCode()) && !oldPerson.getCode().equals(entity.getCode())) {
            tryValidateProperty(null == selectByCode(entity.getCode()), Person.CODE, "auth.person.code.duplicated");
        }

        //原部门编号
        String oldDeptCode = oldPerson.getDepartment() == null ? "" : oldPerson.getDepartment().getCode();

        String updateRt = super.update(entity.getId(), entity);
        if (StringUtils.isNotBlank(updateRt)) {
            //检查是否换部门
            checkPersonChgDepartment(entity, oldDeptCode);
        }
        return updateRt;
    }

    /**
     * 检查是否换部门
     *
     * @param entity
     * @param oldDeptCode
     */
    private void checkPersonChgDepartment(Person entity, String oldDeptCode) {
        String newDeptCode = entity.getDepartment() == null ? "" : entity.getDepartment().getCode();//新部门编号

        if (!StringUtils.equals(newDeptCode, oldDeptCode)) {
            this.eventPublisher.publishEvent(new PersonChgDepartmentEvent(oldDeptCode, newDeptCode));
        }
    }


    /**
     * 导入人员
     *
     * @param dtos
     * @return
     */
    @Override
    public List<? extends PersonImportDTO> importPerson(List<? extends PersonImportDTO> dtos) {
        if (CollectionUtils.isNotEmpty(dtos)) {
            Authentication authentication = Authentication.current().orElseThrow(UnauthorizedException::new);
            int i = 2;
            Map<String, Department> deparmentIds = new HashMap<>();
            List<Person> batch = new ArrayList<>();
            for (PersonImportDTO pi : dtos) {
                Department department = null;
                if (CollectionUtils.isEmpty(pi.getErrors())) {
                    if (StringUtils.isBlank(pi.getCode())) {
                        // pi.getErrors().add(StringUtil.format("auth.person.code.required"));
                    } else {
                        Person currentPerson = selectByCode(pi.getCode());
                        if (currentPerson != null) {
                            pi.getErrors().add(StringUtil.format("auth.person.code.exist"));
                        }
                    }
                    if (pi.getSex() == null) {
                        pi.getErrors().add(StringUtil.format("auth.person.sex.required"));
                    }
                    if (StringUtils.isBlank(pi.getName())) {
                        pi.getErrors().add(StringUtil.format("auth.person.name.required"));
                    }
                    if (StringUtils.isBlank(pi.getDepartmentCode())) {
                        pi.getErrors().add(StringUtil.format("auth.dept.code.required"));
                    } else {
                        if (deparmentIds.containsKey(pi.getDepartmentCode())) {
                            department = deparmentIds.get(pi.getDepartmentCode());
                        } else {
                            department = departmentService.selectByCode(pi.getDepartmentCode());
                        }

                        if (department == null) {
                            pi.getErrors().add(StringUtil.format("auth.dept.notexist"));
                        } else {
                            deparmentIds.put(pi.getDepartmentCode(), department);
                        }
                    }
                    pi.setRownum(i++);
                    if (CollectionUtils.isEmpty(pi.getErrors())) {
                        try {
                            Person person = BeanMapper.deepMap(pi, Person.class);
                            if (deparmentIds.containsKey(pi.getDepartmentCode())) {
                                person.setDepartment(deparmentIds.get(pi.getDepartmentCode()));
                            }
                            if (authentication.getPerson() != null) {
                                person.setCreateDeptCode(authentication.getPerson().getDeptCode());
                            }
                            if (authentication.getUser() != null) {
                                person.setCreatorCode(authentication.getUser().getUsername());
                            }
                            person.setStatus(StatusEnum.ENABLE.ordinal());
                            batch.add(person);
                            if (batch.size() == 2000 || dtos.size() == i - 2) {

                                insert(batch);
                                batch = new ArrayList<>();
                            }
                            // Long id = insert(person);
                            //pi.setId(id);
                        } catch (CommonException e) {
                            logger.error("人员导入出错", e);
                            pi.getErrors().add(e.getMessage());
                        } catch (Exception e) {
                            logger.error("人员导入出错", e);
                            pi.getErrors().add(StringUtil.format("excel.import.error"));
                        }
                    }
                }
            }
        }
        return dtos;
    }

    @Override
    public AuthorityImportExcelResultVo importPerson(MultipartFile file, HttpServletRequest request) throws Exception {
        Locale locale = LocaleContextHolder.getLocale();
        // 获取excel的数据 分为中文版和国际版
        List<? extends PersonImportDTO> list;
        if (locale.getLanguage().equalsIgnoreCase("zh")) {
            list = ExcelUtil.read2Bean(file.getInputStream(), PersonImportDTO.headerMapper, PersonImportDTO.class);
        } else {
            list = ExcelUtil.read2Bean(file.getInputStream(), PersonImportDTO.headerMapper_en, PersonImportDTO.class);
        }
        StringBuilder message = new StringBuilder();

        String serialNo = RandomUtil.randomString(32);
        int succCount = 0;
        int failCount = 0;
        if (CollectionUtils.isNotEmpty(list)) {
            //excel 性别是中文,数据库是1,0
            list.stream().forEach(p -> p.setSex(SexEnum.getIndexByValue(p.getSexFormat())));
            List<? extends PersonImportDTO> listDto = importPerson(list);
            for (PersonImportDTO vo : listDto) {

                if (CollectionUtils.isEmpty(vo.getErrors())) {
                    ImportExcelCommonUtil.setMessage(message, vo.getRownum(), vo.getErrors());
                    succCount++;

                } else {
                    ImportExcelCommonUtil.setMessage(message, vo.getRownum(), vo.getErrors());
                    failCount++;

                }
            }
        }

        return ImportExcelCommonUtil.setImportExcelResult(serialNo, succCount, failCount, message);

    }

    /**
     * 按条件取出人员
     *
     * @param personDtos
     * @return
     */
    private Map<String, List<Person>> setImportPersonMapInfo(List<? extends PersonImportDTO> personDtos) {
        if (CollectionUtils.isNotEmpty(personDtos)) {
            Set<String> personCodes = personDtos.stream().map(PersonImportDTO::getCode).collect(Collectors.toSet());
            if (CollectionUtils.isNotEmpty(personCodes)) {
                List<Person> persons = createQuery().in(Person.CODE, personCodes).listNoPaging();
                if (CollectionUtils.isNotEmpty(persons)) {
                    return persons.stream().collect(groupingBy(Person::getCode));
                }
            }
        }
        return Maps.newHashMap();
    }

    /**
     * 按条件对应的部门
     *
     * @param personDtos
     * @return
     */
    private Map<String, List<Department>> setImportDeptMapInfo(List<? extends PersonImportDTO> personDtos) {
        if (CollectionUtils.isNotEmpty(personDtos)) {
            Set<String> deptCodes = personDtos.stream().map(PersonImportDTO::getDepartmentCode).collect(Collectors.toSet());
            if (CollectionUtils.isNotEmpty(deptCodes)) {
                List<Department> depts = departmentService.createQuery().in(Department.CODE, deptCodes).listNoPaging();
                if (CollectionUtils.isNotEmpty(depts)) {
                    return depts.stream().collect(groupingBy(Department::getCode));
                }
            }
        }
        return Maps.newHashMap();
    }

    /**
     * 检测错误
     *
     * @param personImportDTOs
     * @param personMap
     * @param deptMap
     * @return
     */
    private List<? extends PersonImportDTO> checkImportErrors(List<? extends PersonImportDTO> personImportDTOs,
                                                              Map<String, List<Person>> personMap,
                                                              Map<String, List<Department>> deptMap) {
        for (PersonImportDTO pi : personImportDTOs) {
            // 人员编号 姓名必填
            if (StringUtils.isBlank(pi.getName())) {
                pi.getErrors().add(StringUtil.format("auth.person.name.required"));
            }
            //人员编号 不唯一
            if (StringUtils.isBlank(pi.getCode())) {
                pi.getErrors().add(StringUtil.format("auth.person.code.required"));
            } else {
                List<Person> persons = personMap.get(pi.getCode());
                if (CollectionUtils.isNotEmpty(persons)) {
                    pi.getErrors().add(StringUtil.format("auth.person.code.duplicated"));
                }
            }

            //部门编号不存在
            List<Department> depts = deptMap.get(pi.getDepartmentCode());
            if (CollectionUtils.isEmpty(depts)) {
                pi.getErrors().add(StringUtil.format("auth.dept.code.notexist"));
            } else {
                String deptId = depts.get(0).getId();
                pi.setDepartmentId(deptId);
            }
        }
        return personImportDTOs;
    }


    @Override
    public List<String> getPersonIdsByDeptCode(String deptCode) {
        return personSqlDao.getPersonIdsByDeptCode(deptCode);
    }

    @Override
    public String getUserIdByPersonId(String personId) {
        List<String> userIds = personSqlDao.getUserIdByPersonId(personId);
        if (CollectionUtils.isNotEmpty(userIds)) {
            return userIds.get(0);
        }
        return null;
    }
}
